package com.wenx.mvc.servlet;

import com.wenx.mvc.annotation.*;
import com.wenx.mvc.controller.HelloController;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * tomcat 启动时加载springmvc开发的xxx.war流程
 *
 * tomcat启动阶段：  1. 加载xxx.war
 *                 2. 创建容器:Map iocMap = new HashMap<>();
 *                 3. ScanBasePackage:扫描war下的注解@Controler @Service等
 *                 4. 实例化：将扫描到的类通过反射实例化，并存入到iocMap中
 *                 5. 依赖注入：将存在依赖的bean进行注入
 *                 6. UrlMapping：http请求路径与Method建立联系
 *
 *  tomcat运行阶段： 1. 发送http请求，调用servlet的doGet/doPost方法
 *                 2. 找到从URLMapping中找到对应的method方法对象
 *                 3. 找到method方法后，传参数进行调用
 *                 4. 响应返回结果
 *
 */
public class DispatcherServlet extends HttpServlet {

    //保存待实例化所有类的路径
    List<String> classUrls = new ArrayList<>();

    //ioc容器     key --> helloService    value --> instance
    Map<String, Object> ioc = new HashMap<>();

    //地址映射     key --> /hello/query     value --> method
    Map<String, Object> urlHandlers = new HashMap<>();


    //tomcat初始化ioc
    @Override
    public void init() {
        System.out.println("初始化开始");
        //扫描特殊注解的类
        doScanPackage("com.wenx.mvc");

        //类实例化
        doInstance();

        //处理依赖
        doAutowired();

        //url路径映射
        doUrlMapping();
        System.out.println("初始化结束");
    }

    private void doUrlMapping() {
        ioc.forEach((k,v) -> {
            Class<?> clazz = v.getClass();
            if(clazz.isAnnotationPresent(WXController.class)) {
                //controller
                WXRequestMapping annotation = clazz.getAnnotation(WXRequestMapping.class);
                String classPath = annotation.value();  //hello
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.isAnnotationPresent(WXRequestMapping.class)) {
                        //method存在requestmapping注解
                        WXRequestMapping methodAnnotation = method.getAnnotation(WXRequestMapping.class);
                        String methodPath = methodAnnotation.value();   //query
                        urlHandlers.put(classPath+methodPath,method);
                    }
                }
            }
        });
    }

    private void doAutowired() {

        ioc.forEach((k,v) -> {
            Class<?> clazz = v.getClass();
            if(clazz.isAnnotationPresent(WXController.class)) {
                //controller
                Field[] declaredFields = clazz.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    if (declaredField.isAnnotationPresent(WXAutoWired.class)) {
                        //字段有wxautowired注解
                        WXAutoWired annotation = declaredField.getAnnotation(WXAutoWired.class);
                        String key = annotation.value();//helloservice
                        Object ins = ioc.get(key);
                        //将实例值复制给autowired字段
                        declaredField.setAccessible(true);  //私有属性无法操作，需要设置为true
                        try {
                            declaredField.set(v,ins);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

    }

    //类实例化
    private void doInstance() {

        classUrls.forEach(s -> {
            try {
                //s 包+类名
                Class<?> clazz = Class.forName(s);
                if(clazz.isAnnotationPresent(WXController.class)) {
                    //controller
                    Object newInstance = clazz.newInstance();
                    //获取mapping注解
                    WXRequestMapping annotation = clazz.getAnnotation(WXRequestMapping.class);
                    //value值作为key
                    ioc.put(annotation.value(),newInstance);
                } else if(clazz.isAnnotationPresent(WXService.class)) {
                    //service
                    Object instance = clazz.newInstance();
                    WXService annotation = clazz.getAnnotation(WXService.class);
                    ioc.put(annotation.value(),instance);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

    }

    //扫描basepackage,找到所有class类的全路径
    private void doScanPackage(String basePackage) {
        URL url = this.getClass().getClassLoader().getResource("/" + basePackage.replaceAll("\\.", "/"));
        String fileStr = url.getFile();
        File file = new File(fileStr);
        String[] list = file.list();
        for (String path : list) {
            File filePath = new File(fileStr + path);
            if(filePath.isDirectory()) {
                //文件夹
                doScanPackage(basePackage + "." + path);
            } else {
                //.class文件  com.wenx.mvc.controller.HelloController
                classUrls.add(basePackage+"."+filePath.getName().replace(".class",""));
            }
        }
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    //http://localhost:8080/wvc/hello/query-----dopost
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //super.doPost(req, resp);
        String requestURI = req.getRequestURI();//  /mvc/hello/query
        String contextPath = req.getContextPath();//          /mvc
        String path = requestURI.replace(contextPath,"");

        Method method = (Method) urlHandlers.get(path);

        //这里先写死
        HelloController helloController = (HelloController) ioc.get("/" + path.split("/")[1]);


        Object[] args = hand(req, resp, method);
        try {
            method.invoke(helloController,args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

    }

    private Object[] hand(HttpServletRequest req, HttpServletResponse resp, Method method) {
        //拿到方法有哪些参数
        Class<?>[] parameterTypes = method.getParameterTypes();
        //根据参数的个数，new 一个参数的数组，将方法里的所有参数赋值到args
        Object[] args = new Object[parameterTypes.length];

        int args_i = 0;
        int index = 0;
        for (Class<?> parameterType : parameterTypes) {
            if(ServletRequest.class.isAssignableFrom(parameterType)){
                args[args_i++] = req;
            }
            if(ServletResponse.class.isAssignableFrom(parameterType)){
                args[args_i++] = resp;
            }

            //从0-3判断有没有RequestParam注解，很明显paramterType为0和1时，不是
            //当为2,3时为RequsetParam，需要解析
            //@com.wenx.annotation.WXRequestParam(value=name)
            Annotation[] parameterAnnotation = method.getParameterAnnotations()[index];
            if(parameterAnnotation.length > 0) {
                for (Annotation paramAnnotation : parameterAnnotation) {
                    if(WXRequestParam.class.isAssignableFrom(paramAnnotation.getClass())) {
                        WXRequestParam requestParam = (WXRequestParam) paramAnnotation;
                        //找到注解里的参数值
                        args[args_i++] = req.getParameter(requestParam.value());
                    }
                }
            }
            index++;
        }
        return args;
    }
}
